package com.rtos.model.cpu;

import com.rtos.model.cpu.registers.AX;
import com.rtos.model.cpu.registers.PC;
import com.rtos.model.device.Device;
import com.rtos.model.memory.SystemMemory;
import com.rtos.model.process.PCB;
import com.rtos.model.process.Process;
import com.rtos.model.process.ProcessDispatcher;
import com.rtos.model.process.queue.BlockingQueue;
import com.rtos.model.process.queue.RunningQueue;
import javafx.beans.property.*;
import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author: biubiu0002
 * @Date: 2021-08-30 22:19
 * 懒汉式单例
 **/
@Log
//@Slf4j
public class CPU {
    private static CPU cpu;

    private boolean running;
    private AX ax = AX.getInstance();
    private PC pc = PC.getInstance();

    public IntegerProperty globalTime = new SimpleIntegerProperty(0);
    public IntegerProperty timeSlice = new SimpleIntegerProperty(6);
    public int[] deviceTime = {-1, -1, -1, -1, -1, -1, -1, -1};


    public static int[] psw = {0, 0, 0};

    private byte[] file;
    StringProperty instruction;
    StringProperty axString;
    StringProperty result;
    private final ProcessDispatcher processDispatcher = ProcessDispatcher.getInstance();

    public Process runningProcess = processDispatcher.getHangoutProcess();
    /**
     * CPU执行线程 进程调度线程 锁
     */
    public static Lock lock = new ReentrantLock();
    public static Condition condition1 = lock.newCondition();
    public static Condition condition2 = lock.newCondition();

    private CPU() {
        running = true;
        instruction = new SimpleStringProperty();
        axString = new SimpleStringProperty();
        result = new SimpleStringProperty();


    }

    public static CPU getInstance() {
        if (cpu == null) {
            cpu = new CPU();
        }
        return cpu;

    }

    public StringProperty instructionProperty() {
        return instruction;
    }

    public StringProperty axProperty() {
        return axString;
    }

    public StringProperty resultProperty() {
        return result;
    }

    public IntegerProperty timeSliceProperty() {
        return timeSlice;
    }

    public byte[] getFile() {
        return file;
    }

    public void setFile(byte[] file) {
        this.file = file;
    }

    public boolean isRunning() {
        return running;
    }

    public void setRunning(boolean running) {
        this.running = running;
    }

    /**
     * 用函数 CPU()（该函数没有参数）模拟单个中央处理器。
     * 该函数主要负责解释“可执行文件”中的指令。（为简单计，用户命令接口部分实现的命令不必用 CPU()解释执行）
     */


    public void cpuRun() {

        /**
         * 一个系统时间执行一条指令
         * 不要将获取锁的过程写在try块，因为如果在获取锁时发生了异常，抛出异常的同时会导致锁无故释放
         */

        lock.lock();
        try {
            condition1.await();
            if(cpu.isRunning()){
                //先进入锁中再执行业务
                checkPSW();

                //回复寄存器
                Process process = processDispatcher.getRunningProcess();
                if (process != runningProcess) {
                    this.reloadRegisters(process);
                }

                /**
                 * 判断中断
                 */

                /**
                 * 确认是否为闲置进程
                 */
                if (runningProcess != processDispatcher.getHangoutProcess()) {
                    boolean endFlag = false;
                    PCB pcb = runningProcess.getPcb();
                    int instruction = file[pc.getBuffer()];
                    pc.plus();

                    pcb.restRunTimeProperty().set(pcb.restRunTimeProperty().get() - 1);
                    pcb.lastRunTimeProperty().set(pcb.lastRunTimeProperty().get() + 1);
//                    System.out.println("进程#" + pcb.getPID() + "的totalRunTime=" + pcb.totalRunTimeProperty().get() + " restRunTime=" + pcb.restRunTimeProperty().get()
//                            + " lastRunTime=" + pcb.lastRunTimeProperty().get());


                    //x++指令
                    if (instruction == 0) {
//                AX++;
                        ax.setBuffer(ax.getBuffer() + 1);
//                        System.out.println("x++");
//                IR = "x++";
                        cpu.instructionProperty().setValue("x++");
                        cpu.axProperty().setValue("x=" + ax.getBuffer());


                    }
                    //x--指令
                    else if (instruction == 32) {
                        ax.setBuffer(ax.getBuffer() - 1);
//                        System.out.println("x--");

                        cpu.instructionProperty().setValue("x--");
                        cpu.axProperty().setValue("x=" + ax.getBuffer());


                    }
                    //end指令
                    else if (instruction == 96) {


//                        System.out.println("end");
                        psw[0] = 1;

                        cpu.instructionProperty().setValue("end");
                        cpu.axProperty().setValue("x=" + ax.getBuffer());
                        endFlag = true;

                    }

                    //x=?指令
                    else if (instruction < 0) {
                        ax.setBuffer(instruction + 128);
//                        System.out.println("x=" + ax.getBuffer());
                        cpu.instructionProperty().setValue("x=" + ax.getBuffer());
                        cpu.axProperty().setValue("x=" + ax.getBuffer());


//                AX = i + 128;
//                IR = "x=" + AX;
                    }
                    //！？？指令
                    else {
                        /**
                         * code：是否为特殊
                         */
                        int code = instruction / 64;
                        int[] device = {(instruction % 64) / 16, (instruction % 64) / 8};
                        int[] time = {instruction % 16, instruction % 8};
                        //保存寄存器内容到PCB
                        stashRegisters(runningProcess);
                        //设置pcb IO阻塞时间
                        pcb.blockingTimeProperty().setValue(time[code]);
                        if (device[code] == 0) {
                            //占用了哪个设备
                            pcb.deviceTypeProperty().setValue("A");
//                            System.out.println("!A" + time[code]);
                            cpu.instructionProperty().setValue("!A" + time[code]);
                            //把进程阻塞
                            int deviceIndex = processDispatcher.block("A");


                            if (deviceIndex < 9) {
                                deviceTime[deviceIndex - 1] = time[code];
                            }


                        }
                        if (device[code] == 1) {
                            pcb.deviceTypeProperty().setValue("B");
//                            System.out.println("!B" + time[code]);
                            cpu.instructionProperty().setValue("!B" + time[code]);
                            //把进程阻塞
                            int deviceIndex = processDispatcher.block("B");
                            if (deviceIndex < 9) {
                                deviceTime[deviceIndex - 1] = time[code];
                            }
//                    IR = "!B" + time[code];
                        }
                        if (device[code] == 2) {
                            pcb.deviceTypeProperty().setValue("C");
//                            System.out.println("!C" + time[code]);
                            cpu.instructionProperty().setValue("!C" + time[code]);
                            /**
                             * 当前运行进程换到阻塞队列
                             */
                            int deviceIndex = processDispatcher.block("C");
                            if (deviceIndex < 9) {
                                deviceTime[deviceIndex - 1] = time[code];
                            }
                        }
                        /**
                         * 换上新进程运行
                         */
                        Process nextRunningProcess = processDispatcher.readyProcessToRun();
                        this.reloadRegisters(nextRunningProcess);

                    }

//                if(!endFlag){
//
//                }

                }

            }





        } catch (Exception e) {
            e.printStackTrace();
        } finally {

            condition2.signalAll();
            lock.unlock();


        }


    }

    /**
     * 中断的发现应该是硬件的工作，但在这里，用在函数
     * CPU()中检测 PSW 的方式来模拟
     */
        public synchronized void checkPSW() {
            if (psw[0] == 1) {
                programEndInterruption();
            }
            if (psw[1] == 1) {
                timeSliceEndInterruption();
            }
            if (psw[2] == 1) {
                IOInterruption();
            }
        }

    public synchronized void programEndInterruption() {
        log.info("进程#" + runningProcess.getPcb().getPID() + "完成");

        cpu.resultProperty().set(cpu.axProperty().get());

        processDispatcher.destory(runningProcess);
        //恢复寄存器
        /**
         * 1.RunningQueue.remove() 销毁进程;
         * 2.readyProcessToRun()
         */

        Process process = processDispatcher.readyProcessToRun();
        if (process != runningProcess) {
            this.reloadRegisters(process);
        }
        psw[0] = 0;
//        System.out.println("进程结束处理完成");

    }

    public synchronized void timeSliceEndInterruption() {

//        runningProcess = RunningQueue.getRunningProcess();
        /**
         * runningProcessToReady() 当前运行进程换下
         * readyProcessToRun() 就绪队列队头换上
         */
        runningProcess.getPcb().lastRunTimeProperty().set(0);
        stashRegisters(runningProcess);
        processDispatcher.runningProcessToReady(runningProcess);


        /**
         * 换上新进程
         */
        Process readyProcess = processDispatcher.readyProcessToRun();
        reloadRegisters(readyProcess);

        psw[1] = 0;
//        System.out.println("时间片中断处理完成");


    }

    public void IOInterruption() {
        /**
         * 判断哪个设备已使用完
         */
        if (deviceTime[0] == 0) {
            String a1 = Device.getInstance().usingProcesses(1);
            psw3Util(a1);
            deviceTime[0]--;
        }
        if (deviceTime[1] == 0) {
            String a2 = Device.getInstance().usingProcesses(2);
            psw3Util(a2);
        }
        if (deviceTime[2] == 0) {
            String b1 = Device.getInstance().usingProcesses(3);
            psw3Util(b1);
        }
        if (deviceTime[3] == 0) {
            String b2 = Device.getInstance().usingProcesses(4);
            psw3Util(b2);
        }
        if (deviceTime[4] == 0) {
            String b3 = Device.getInstance().usingProcesses(5);
            psw3Util(b3);
        }
        if (deviceTime[5] == 0) {
            String c1 = Device.getInstance().usingProcesses(6);
            psw3Util(c1);
        }
        if (deviceTime[6] == 0) {
            String c2 = Device.getInstance().usingProcesses(7);
            psw3Util(c2);
        }
        if (deviceTime[7] == 0) {
            String c3 = Device.getInstance().usingProcesses(8);
            psw3Util(c3);
        }
        psw[2] = 0;
//        System.out.println("IO中断处理完成");

    }


    private void psw3Util(String pid) {
        for (int i = 0; i < BlockingQueue.size(); i++) {
//            System.out.println("pid = " + pid);
            if (!"".equals(pid)) {
                int pidNum = Integer.parseInt(pid);
//                System.out.println("pidNum = " + pidNum);
                if (BlockingQueue.get(i).getPid().get() == pidNum) {
                    Process process = BlockingQueue.get(i);
                    /**
                     * 让刚从阻塞队列移除的进程进入就绪队列
                     */
                    processDispatcher.blockingProcessToReady(process);
                    break;
                }
            }

        }
    }

    public void stashRegisters(Process process) {
        //第一步，保存运行进程的 CPU 现场；
        PCB pcb = process.getPcb();
        pcb.setAXBuffer(AX.getInstance().getBuffer());
        pcb.setPCBuffer(PC.getInstance().getBuffer());
//        System.out.println("stash process" + process);

    }

    public void reloadRegisters(Process process) {
        //第一步，保存运行进程的 CPU 现场；
        PCB pcb = process.getPcb();
        AX.getInstance().setBuffer(pcb.getAXBuffer());
        PC.getInstance().setBuffer(pcb.getPCBuffer());
        runningProcess = process;
        timeSlice.set(6);
        this.setFile(process.getExec());
//        System.out.println("reload process" + process);

    }

    /**
     * 时间运行函数
     */
    public void time() {
        try {
            lock.lock();
            globalTime.set(globalTime.get() + 1);
            if (runningProcess != processDispatcher.getHangoutProcess()) {
                timeSlice.set(timeSlice.get() - 1);
            }
            if (timeSlice.get() == 0) {
                timeSlice.set(6);
                psw[1] = 1;
            }
            if (deviceTime != null) {
                for (int i = 0; i < deviceTime.length; i++) {
//                    String s = Device.getInstance().usingProcesses(i + 1);
//                    if(!s.equals("")){
//                        deviceTime[i]--;
//                    }
                    deviceTime[i]--;

                    if (deviceTime[i] == 0) {
                        psw[2] = 1;
                    }
                }
            }
            this.blockingProcessTimeMinus();


        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            condition1.signal();

            lock.unlock();


        }

    }

    //synchronized防止被JavaFX线程抢占
    private synchronized void blockingProcessTimeMinus() {
        /**
         * 每个设备正在占用的进程
         */
        for (int i = 1; i <= 8; i++) {
            String pid = Device.getInstance().usingProcesses(i);
            //设备空闲时 pid为""
            if (!"".equals(pid)) {
                //正在占用的进程pid
                PCB pcb = SystemMemory.getPCB(Integer.parseInt(pid));
                if (pcb != null) {
                    pcb.blockingTimeProperty().set(pcb.blockingTimeProperty().get() - 1);
                }
            }

        }

    }


}
